import numpy as np


def show_ls(ls):
    for i in ls:
        print(i)


def to_np(ls: list):
    if not isinstance(ls[0], (list, np.ndarray)):
        return np.array(ls)

    ret = []
    for i in ls:
        if not isinstance(i, np.ndarray):
            r = np.array(i)
        else:
            r = i
        ret.append(r)
    return ret


def getFarthestPointInDirection(aa, d):
    # 多边形aa在d方向上最远的点
    index_max = aa.dot(d).argmax()
    ret = aa[index_max]
    # aa = np.delete(aa, index_max, axis=0)     # 删除用过的点, 但函数内部变量暂时无法实现
    index_ls = list(range(0, aa.shape[0]))
    index_ls.remove(index_max)
    aa = aa[index_ls, :]
    if 0:
        import numpy as np
        aa = np.array([
            [1, 1, 1],
            [2, 2, 2],
            [3, 3, 3],
            [4, 4, 4],
        ])
        index_max = 1
        index_ls = list(range(0, aa.shape[0]))
        index_ls.remove(index_max)
        aa = aa[index_ls, :]
    return ret


def support(aa, bb, d):
    # 获得支持向量
    if not isinstance(d, np.ndarray):
        d = np.array(d)
    p1 = getFarthestPointInDirection(aa, d)
    p2 = getFarthestPointInDirection(bb, -d)
    p3 = p1 - p2
    return p3


def conv_to_np(a, b):
    if not isinstance(a, np.ndarray) or isinstance(b, np.ndarray):
        a = np.array(a)
        b = np.array(b)
    return a, b


def get_cos_theta(ab, ao):
    """
    求两向量的夹角余弦值

    ps: a在原点的时候没有考虑!
    """
    if (ao[:2] == np.array([0, 0])).all() or (ab[:2] == np.array([0, 0])).all():      # a为原点时则随机返回None
        return None
    cos = round(ab.dot(ao) / (np.linalg.norm(ab) * np.linalg.norm(ao)) - 1, 6)  # 只能用近似值判断是否为1
    return cos


def get_d_bycross(a, b, norm=True):
    """
    # 得到垂直于ab且朝原点的法向量oc, 即d
    """
    a, b = conv_to_np(a, b)
    # if a[0] == 0 and a[1] == 0:
    #     #
    #     print('======= 在原点的情况, 则随便返一个方向 ========')
    #     return np.array([0, 1])

    # 二维转三维
    ret_size = a.size
    if ret_size == 2:
        a = np.hstack([a, 0])
        b = np.hstack([b, 0])

    ab = b - a
    ao = -a

    # 根据cos_theta的值判断平行/重叠的情况
    cos_theta = get_cos_theta(ab, ao)
    if cos_theta is None:
        # a或b和原点重叠的情况, 随机返回一个方向
        print('======= 在原点的情况, 则随便返一个方向 ========')

        return to_np([1, 0])

    if cos_theta == 0:
        # 平行的情况, 返回(y, -x)或(-y, x)中任意一个
        x, y = ab[:2]
        return to_np([y, -x])

    zz = np.cross(ab, ao)
    zz = np.sign(zz)

    oc = np.cross(zz, ab)
    if norm:
        oc = oc/np.linalg.norm(oc)      # 标准化

    ret = oc[:-1]
    return ret     # 变回2维


def cross_2d(a, b, norm=True):
    """
    2d向量的外积.

    # 将二维向量拓展为三维后, 返回三维向量
    """
    a, b = conv_to_np(a, b)

    # 二维变三维
    if a.size == 2:
        a = np.hstack([a, 0])
    if b.size == 2:
        b = np.hstack([b, 0])

    # 根据cos_theta的值判断平行/重叠的情况
    cos_theta = get_cos_theta(a, b)
    if cos_theta is None:
        # a或b和原点重叠的情况, 随机返回一个方向
        return to_np([1, 0])

    if cos_theta == 0:
        # 平行的情况, 返回(y, -x)或(-y, x)中任意一个
        x, y = a[:2]
        return to_np([y, -x])

    oc = np.cross(a, b)
    if norm:
        oc = oc / np.linalg.norm(oc)  # 标准化

    ret = oc[:2]
    return ret


def get_abc_cross(a, b, c):
    """
    得到a&b&c的外积(叉乘), 即AxBxC

    # 获得二维平面中, 三个向量的连续外积的结果.
    # 将返回一个二维向量, 如ABN=A*B*B的向量垂直于B,
    # 然后判断ABN和AO的内积, 大于0则说明原点在ABN方向, 否则在反方向.
    """
    # return cross_2d(cross_2d(a, b), c)[:2]

    # 二维变三维
    if a.size == 2:
        a = np.hstack([a, 0])
    if b.size == 2:
        b = np.hstack([b, 0])
    if c.size == 2:
        c = np.hstack([c, 0])

    zz = np.cross(np.cross(a, b), c)
    ret = zz / np.linalg.norm(zz)
    ret = ret[:2]
    return ret
